package edu.ut.ece382c.valloy.hierarchy;

import java.util.ArrayList;
import java.util.List;

public class StringTree extends Tree<String> 
{
	public StringTree() 
	{
		super();
	}
	
	public static StringTree recursivelyGenerateHierarchy(StringTree tree, String[] classes_array)
	{
		if (tree.isEmpty())
		{
			if (classes_array == null)
				return tree;
			else if ((getNumberOfElementsThatContainExtends(classes_array) + 1) != classes_array.length)
				System.out.println("Error: Class hierarchy is incorrect.  You must have at least 1 class declaration, 1 root class (class that does not extend any other class), and all non-root classes extending 1 other class.");
			else if (classes_array.length == 1)
			{
				String classname = classes_array[0].substring(classes_array[0].indexOf("class ")+6, classes_array[0].indexOf("{")).trim() ;
				tree.setRootElement(new StringNode(classname, null));
				return tree;
			}
			else 
			{
				tree.setRootElement(new StringNode(getElementThatDoesNotContainExtends(classes_array), null));
				String[] new_classes_array = new String[classes_array.length - 1];
				int counter = 0;
				for (String aClass: classes_array)
					if (aClass.contains("extends"))
						new_classes_array[counter++] = aClass;
				return(recursivelyGenerateHierarchy(tree, new_classes_array));
			}
		}
		else if(classes_array.length == 1)
		{
			String classname = classes_array[0].substring(classes_array[0].indexOf("class ")+6, classes_array[0].indexOf(" extends")).trim();
			String parentclassname = classes_array[0].substring(classes_array[0].indexOf("extends ")+8, classes_array[0].indexOf("{")).trim();
			Node<String> parent = tree.getNodeByName(parentclassname);
			if (parent != null)
			{
				parent.addChild(new StringNode(classname, parent));
				return tree;
			}
			else
				System.out.println("Error: Class hierarchy is incorrect.  You have a dangling class that is not tied into the class hierarchy.");
		}
		else if (classes_array.length > 1)
		{
			String classname = classes_array[0].substring(classes_array[0].indexOf("class ")+6, classes_array[0].indexOf(" extends")).trim();
			String parentclassname = classes_array[0].substring(classes_array[0].indexOf("extends ")+8, classes_array[0].indexOf("{")).trim();
			Node<String> parent = tree.getNodeByName(parentclassname);
			if (parent != null)
			{
				parent.addChild(new StringNode(classname, parent));
				String[] new_classes_array = new String[classes_array.length - 1];
				System.arraycopy(classes_array, 1, new_classes_array, 0, classes_array.length - 1);
				return(recursivelyGenerateHierarchy(tree, new_classes_array));
			}				
			else
				System.out.println("Error: Class hierarchy is incorrect.  Some class(es) are not tied into the class hierarchy.");
		}
		else
			System.out.println("Error: You have no classes declared!  You must declare at least 1 class.");
		return null;//something is wrong if it ever gets to here.
	}
	
	public Node<String> getNodeByName(String aClass)
	{
		List<Node<String>> list = new ArrayList<Node<String>>();
		this.walk2(rootElement, list, aClass);
		return list.get(0);		
	}
	
	public static List<Node<String>> getAllDescendantsOfNode(String nodeValue, StringTree aTree)
	{
		List<Node<String>> list = new ArrayList<Node<String>>();
		aTree.walk4(aTree.getNodeByName(nodeValue), list);
		return list;
	}
		
	public static String getParentNodeNameByChildNodeName(String childNodeName, StringTree aTree)
	{
		if (childNodeName.equals(aTree.getRootElement().getData()))
			return null;
		List<Node<String>> list = new ArrayList<Node<String>>();
		aTree.walk3(aTree.getRootElement(), list, childNodeName);
		return list.get(0).getData();			
	}
	
	public static int getNumberOfElementsThatContainExtends(String[] classes_array)
	{
		int counter = 0;
		for(String aClass:classes_array)
			if (aClass.contains("extends"))
				counter++;
		return counter;
	}
	
	public static String getElementThatDoesNotContainExtends(String[] classes_array)
	{
		for(String aClass:classes_array)
			if (!aClass.contains("extends"))
				return aClass.substring(aClass.indexOf("class ")+6, aClass.indexOf("{")).trim();
		return null;
	}
	
	public static String getNearestParentNodeThatDefinesFunction(Node<String> aNode, String functionPart, StringTree myTree, String[] virtualFunctions)
	{
		List<Node<String>> list = new ArrayList<Node<String>>();
		myTree.walk5(aNode, list, virtualFunctions, functionPart);
		return list.get(0).getData();
	}
	
	public static String highestNodeInHierarchyThatDefinesFunction(String functionPart, StringTree myTree, String[] virtualFunctions)
	{
		List<Node<String>> list = new ArrayList<Node<String>>();
		myTree.walk6(myTree.getRootElement(), functionPart, virtualFunctions, list);
		return list.get(0).getData();
	}
	
	private void walk6(Node<String> aNode, String functionPart, String[] virtualFunctions, List<Node<String>> list)
	{
		for (String aVirtualFunction:virtualFunctions)
		{
			if (aVirtualFunction.contains(aNode.getData() + "." + functionPart))
			{
				list.add(aNode);
				return;
			}
		}
		for (Node<String> child : aNode.getChildren())
			walk6(child, functionPart, virtualFunctions, list);
	}
	
	private void walk5(Node<String> aNode, List<Node<String>> list, String[] virtualFunctions, String functionPart)
	{
		for (String aVirtualFunction:virtualFunctions)
		{
			if (aVirtualFunction.contains(aNode.getData() + "." + functionPart))
			{
				list.add(aNode);
				return;
			}
		}
		walk5(aNode.parent, list, virtualFunctions, functionPart);
	}
	
	private void walk4(Node<String> element, List<Node<String>> list)
	{
		for (Node<String> aNode : element.getChildren()) {
			list.add(aNode);
			walk4(aNode, list);
		}
		return;
	}
	
	private void walk3(Node<String> element, List<Node<String>> list, String childNodeName) 
	{
		for (Node<String> aNode : element.getChildren()) {
        	if (aNode.data.equals(childNodeName))
        	{
        		list.add(element);
        		return;
        	}
            walk3(aNode, list, childNodeName);
        }
    }
	
	private void walk2(Node<String> element, List<Node<String>> list, String aClass) 
	{
		if (element.getData().equals(aClass))
			list.add(element);
        for (Node<String> data : element.getChildren()) 
            walk2(data, list, aClass);
    }
}
